package mt.omid.jscap.operaion;

/****
 * Create by Omid Mehdizadeh
 * Copyright 2010, Omid Mehdizadeh
 * Based on JDiameter Open Source Project
 */



import static mt.omid.jscap.SCAPCode.*;
import static mt.omid.jscap.SCAPCode.END_USER_MSISDN;
import static mt.omid.jscap.SCAPCode.ERICSSON_VENDOR_ID;
import static mt.omid.jscap.SCAPCode.RECORD_TYPE_EVENT;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Event_Timestamp;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Requested_Action;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Requested_Service_Unit;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Subscription_Id;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Subscription_Id_Data;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Subscription_Id_Type;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Unit_Type;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Unit_Value;
import static mt.omid.jscap.SCAPCode.SCAP_AVP_Value_Digits;
import static mt.omid.jscap.SCAPCode.SERVICE_UNIT_TIME;
import static mt.omid.jscap.SCAPCode.SRV_PRM_INF_SERVICE_PROVIDER_ID;
import static mt.omid.jscap.SCAPCode.SRV_PRM_INF_TRAFFIC_CASE;
import static org.jdiameter.api.Avp.ACCT_APPLICATION_ID;
import static org.jdiameter.api.Avp.ACC_RECORD_NUMBER;
import static org.jdiameter.api.Avp.ACC_RECORD_TYPE;
import static org.jdiameter.api.Avp.ORIGIN_HOST;
import static org.jdiameter.api.Avp.ORIGIN_REALM;
import static org.jdiameter.api.Avp.VENDOR_ID;
import static org.jdiameter.api.Avp.VENDOR_SPECIFIC_APPLICATION_ID;
import static org.jdiameter.api.Message.ACCOUNTING_REQUEST;

import java.util.Date;
import java.util.List;

import mt.omid.jscap.Tools;

import org.jdiameter.api.ApplicationId;
import org.jdiameter.api.AvpSet;
import org.jdiameter.api.IllegalDiameterStateException;
import org.jdiameter.api.InternalException;
import org.jdiameter.api.Message;
import org.jdiameter.api.OverloadException;
import org.jdiameter.api.Request;
import org.jdiameter.api.RouteException;
import org.jdiameter.api.Session;
import org.jdiameter.api.SessionFactory;
import org.jdiameter.api.app.AppSession;
import org.jdiameter.client.api.IContainer;
import org.jdiameter.client.impl.app.sh.ShClientSessionImpl;
import org.jdiameter.common.impl.app.AppSessionImpl;
import org.jdiameter.common.impl.app.sh.UserDataRequestImpl;

import com.sun.tools.example.debug.bdi.SessionListener;


public class SCAPSession extends AppSessionImpl
{

    //    private Request request;
    //    private Session session;
    private boolean    stateless = false;
    private IContainer stack;
    private long       acctAppId;
    private int recNum = 0;


    SCAPSession( IContainer stack, String sessionId, SessionFactory sf, SessionListener sl )
	    throws InternalException
    {
	this.stack = stack;
	if( sessionId == null )
	    this.session = sf.getNewSession();
	else
	    this.session = sf.getNewSession( sessionId );
	this.acctAppId = stack.getMetaData().getLocalPeer()
	.getCommonApplications().iterator().next().getAcctAppId();
    }

    public void send( Message msg ) throws InternalException, IllegalDiameterStateException,
	    RouteException, OverloadException
    {
	this.session.send( msg );
    }

    public Request createDirectDebitTimeRequest( String msisdn, long duration, String destRealm )
	    throws InternalException
    {
	// Create Request	    
	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
		.createByAccAppId( 0 ), destRealm );
	req.setProxiable( true );

	// Add required AVPs for SCAP
	addCommonEventAvp( req );

	// Add Subscription ID AVP
	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
		true, false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
		false );
	subId
		.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
			false );

	// Add Requested Unit
	AvpSet srvUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Requested_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	srvUnit.addAvp( SCAP_AVP_Unit_Type, SERVICE_UNIT_TIME, ERICSSON_VENDOR_ID, true, false );
	srvUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, ( long ) duration, ERICSSON_VENDOR_ID, true, false );

	return req;
    }

    public Request createDirectDebitVolumeRequest( String msisdn, long vol, String destRealm )
	    throws InternalException
    {
	// Create Request	    
	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
		.createByAccAppId( 0 ), destRealm );
	req.setProxiable( true );

	// Add required AVPs for SCAP
	addCommonEventAvp( req );

	// Add Subscription ID AVP
	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
		true, false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
		false );
	subId
		.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
			false );

	// Add Requested Unit
	AvpSet srvUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Requested_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	srvUnit.addAvp( SCAP_AVP_Unit_Type, SERVICE_UNIT_VOLUME, ERICSSON_VENDOR_ID, true, false );
	srvUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, ( long ) vol, ERICSSON_VENDOR_ID, true, false );

	return req;
    }

    public Request createDirectDebitEventRequest( String msisdn, long number, String destRealm )
	    throws InternalException
    {
	// Create Request	    
	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
		.createByAccAppId( 0 ), destRealm );
	req.setProxiable( true );

	// Add required AVPs for SCAP
	addCommonEventAvp( req );

	// Add Subscription ID AVP
	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
		true, false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
		false );
	subId
		.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
			false );

	// Add Requested Unit
	AvpSet srvUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Requested_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	srvUnit.addAvp( SCAP_AVP_Unit_Type, SERVICE_UNIT_EVENT, ERICSSON_VENDOR_ID, true, false );
	srvUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, ( long ) number, ERICSSON_VENDOR_ID, true, false );

	return req;
    }

    private void addCommonEventAvp( Request req )
    {
	// Replace Origin Host with SCAP format
	req.getAvps().removeAvp( ORIGIN_HOST );
	req.getAvps().removeAvp( ORIGIN_REALM );

	req.getAvps().addAvp( ORIGIN_HOST, stack.getMetaData().getLocalPeer().getUri().toString(),
		true, false, false );
	req.getAvps().addAvp( ORIGIN_REALM, stack.getMetaData().getLocalPeer().getRealmName(),
		true, false, false );

	// Add Vendor Specific ID
	AvpSet vndApp = req.getAvps().addGroupedAvp( VENDOR_SPECIFIC_APPLICATION_ID, true, false );
	vndApp.addAvp( VENDOR_ID, stack.getMetaData().getLocalPeer().getVendorId(), true, false,
		true );
	vndApp.addAvp( ACCT_APPLICATION_ID, this.acctAppId, true, false, true );

	// Add Record Type AVP
	req.getAvps().addAvp( ACC_RECORD_TYPE, RECORD_TYPE_EVENT, true, false );

	// Add Record Number
	req.getAvps().addAvp( ACC_RECORD_NUMBER, 0, true, false );

	// Add Event-Timestamp
	req.getAvps()
		.addAvp( SCAP_AVP_Event_Timestamp, new Date(), ERICSSON_VENDOR_ID, true, false );

	// Add Requested Action
	req.getAvps().addAvp( SCAP_AVP_Requested_Action, ACTION_DIRECT_DEBIT, ERICSSON_VENDOR_ID,
		true, false );

	// Add Service Parameter Info AVPs
	Tools.addServiceParamInfo( req, SRV_PRM_INF_SERVICE_PROVIDER_ID, "0" );
	Tools.addServiceParamInfo( req, SRV_PRM_INF_TRAFFIC_CASE, "20" );
    }

    public Request createChargingSessionStartRequest( String msisdn, String destRealm,
						      int unitType, long unitNum ) throws InternalException
    {
	// Create Request	    
	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
		.createByAccAppId( 0 ), destRealm );
	req.setProxiable( true );

	// Add required AVPs for SCAP Session
	addCommonSessionAvp( req );

	// Add Record Type AVP
	req.getAvps().addAvp( ACC_RECORD_TYPE, RECORD_TYPE_START, true, false );

	// Add Record Number
	req.getAvps().addAvp( ACC_RECORD_NUMBER, 0, true, false );

	// Add Subscription ID AVP
	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
		true, false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
		false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
			false );

	// Add Requested Unit
	AvpSet srvUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Requested_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	srvUnit.addAvp( SCAP_AVP_Unit_Type, unitType, ERICSSON_VENDOR_ID, true, false );
	srvUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, unitNum, ERICSSON_VENDOR_ID, true, false );

	return req;
    }

    public Request createChargingSessionInterimRequest( String msisdn, String destRealm,
						      int unitType, long unitNum, long usedNum ) throws InternalException
    {
	// Create Request	    
	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
		.createByAccAppId( 0 ), destRealm );
	req.setProxiable( true );

	// Add required AVPs for SCAP Session
	addCommonSessionAvp( req );

	// Add Record Type AVP
	req.getAvps().addAvp( ACC_RECORD_TYPE, RECORD_TYPE_INTERIM, true, false );

	// Add Record Number
	req.getAvps().addAvp( ACC_RECORD_NUMBER, ++this.recNum, true, false );

	// Add Subscription ID AVP
	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
		true, false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
		false );
	subId.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
			false );

	// Add Requested Unit
	AvpSet srvUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Requested_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	srvUnit.addAvp( SCAP_AVP_Unit_Type, unitType, ERICSSON_VENDOR_ID, true, false );
	srvUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, unitNum, ERICSSON_VENDOR_ID, true, false );

	// Add Used Unit
	AvpSet usdUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Used_Service_Unit,
		ERICSSON_VENDOR_ID, true, false );
	usdUnit.addAvp( SCAP_AVP_Unit_Type, unitType, ERICSSON_VENDOR_ID, true, false );
	usdUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
		SCAP_AVP_Value_Digits, usedNum, ERICSSON_VENDOR_ID, true, false );

	return req;
    }

    public Request createChargingSessionStopRequest( String msisdn, String destRealm,
  						      int unitType, long usedNum ) throws InternalException
      {
  	// Create Request	    
  	Request req = this.session.createRequest( ACCOUNTING_REQUEST, ApplicationId
  		.createByAccAppId( 0 ), destRealm );
  	req.setProxiable( true );

  	// Add required AVPs for SCAP Session
  	addCommonSessionAvp( req );

  	// Add Record Type AVP
  	req.getAvps().addAvp( ACC_RECORD_TYPE, RECORD_TYPE_STOP, true, false );

  	// Add Record Number
  	req.getAvps().addAvp( ACC_RECORD_NUMBER, ++this.recNum, true, false );

  	// Add Subscription ID AVP
  	AvpSet subId = req.getAvps().addGroupedAvp( SCAP_AVP_Subscription_Id, ERICSSON_VENDOR_ID,
  		true, false );
  	subId.addAvp( SCAP_AVP_Subscription_Id_Type, END_USER_MSISDN, ERICSSON_VENDOR_ID, true,
  		false );
  	subId.addAvp( SCAP_AVP_Subscription_Id_Data, msisdn, ERICSSON_VENDOR_ID, true, false,
  			false );

  	// Add Used Unit
  	AvpSet usdUnit = req.getAvps().addGroupedAvp( SCAP_AVP_Used_Service_Unit,
  		ERICSSON_VENDOR_ID, true, false );
  	usdUnit.addAvp( SCAP_AVP_Unit_Type, unitType, ERICSSON_VENDOR_ID, true, false );
  	usdUnit.addGroupedAvp( SCAP_AVP_Unit_Value, ERICSSON_VENDOR_ID, true, false ).addAvp(
  		SCAP_AVP_Value_Digits, usedNum, ERICSSON_VENDOR_ID, true, false );

  	return req;
      }
    
    private void addCommonSessionAvp( Request req )
    {
	// Replace Origin Host with SCAP format
	req.getAvps().removeAvp( ORIGIN_HOST );
	req.getAvps().removeAvp( ORIGIN_REALM );

	req.getAvps().addAvp( ORIGIN_HOST, stack.getMetaData().getLocalPeer().getUri().toString(),
		true, false, false );
	req.getAvps().addAvp( ORIGIN_REALM, stack.getMetaData().getLocalPeer().getRealmName(),
		true, false, false );

	// Add Vendor Specific ID
	AvpSet vndApp = req.getAvps().addGroupedAvp( VENDOR_SPECIFIC_APPLICATION_ID, true, false );
	vndApp.addAvp( VENDOR_ID, stack.getMetaData().getLocalPeer().getVendorId(), true, false,
		true );
	vndApp.addAvp( ACCT_APPLICATION_ID, this.acctAppId, true, false, true );

	// Add Event-Timestamp
	req.getAvps()
		.addAvp( SCAP_AVP_Event_Timestamp, new Date(), ERICSSON_VENDOR_ID, true, false );

	// Add Requested Action
//	req.getAvps().addAvp( SCAP_AVP_Requested_Action, ACTION_DIRECT_DEBIT, ERICSSON_VENDOR_ID,
//		true, false );

	// Add Service Parameter Info AVPs
	Tools.addServiceParamInfo( req, SRV_PRM_INF_SERVICE_PROVIDER_ID, "0" );
	Tools.addServiceParamInfo( req, SRV_PRM_INF_TRAFFIC_CASE, "20" );
    }

    @Override
    public boolean isStateless()
    {
	return this.stateless;
    }

}
